#include "alphafield.h"

QAlphaField::QAlphaField(QWidget *parent):
        QWidget(parent)
{
    f.Create(5,5);
    setAttribute(Qt::WA_StaticContents);
    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
    ch = NULL;
    setMouseTracking(true);
    status = afterReset;
}

QSize QAlphaField::getFieldSize() const
{
    return QSize(f.Width(), f.Height());
}

void QAlphaField::setFieldSize(const QSize &size)
{
    f.Create(size.width(), size.height());
    update();
}

int QAlphaField::cellSize() const
{
    QFontMetrics metrics(font());
    return (metrics.height()+2);
}

QSize QAlphaField::sizeHint() const
{
    QSize size = QSize(f.Width(), f.Height()) * cellSize();
    size += QSize(1,1);
    return size;
}

void QAlphaField::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QBrush normalBrush(QColor::fromRgb(200, 200, 255));
    QBrush hlBrush(QColor::fromRgb(100, 100, 255));
    QBrush selBrush(QColor::fromRgb(200, 255, 200));
    QBrush hlselBrush(QColor::fromRgb(100, 255, 100));

    QBrush newAlphaBrush(QColor::fromRgb(255, 200, 200));
    QBrush hlNewAlphaBrush(QColor::fromRgb(255,100,100));

    QPen normPen = painter.pen();
    QPen naPen(QColor::fromRgb(200, 0, 0));

    int s = cellSize();
    QString str;
    for ( int y = 0, i = 0; i < f.Height(); i++, y += s)
        for( int x = 0, j = 0; j < f.Width(); j++, x += s)
        {
            deque<solver::Coords>::iterator fres;
            fres = std::find(sel.begin(), sel.end(), solver::Coords(j, i));
            switch(status)
            {
                case alphaSet:
                if(fres != sel.end())
                {
                    if(i == chosenY && j == chosenX)
                        painter.setBrush(hlselBrush);
                    else painter.setBrush(selBrush);
                    break;
                }
                case afterReset:
                    if(i == chosenY && j == chosenX)
                        if(fres != sel.end())
                            painter.setBrush(hlNewAlphaBrush);
                        else
                            painter.setBrush(hlBrush);
                    else
                        if(fres != sel.end())
                            painter.setBrush(newAlphaBrush);
                        else
                            painter.setBrush(normalBrush);
                break;
                default:
                break;
            }
            painter.drawRect(x, y, s, s);
            if(f[i][j] != L' ')
            {
                QFontMetrics metrics(font());
                QSize csize = metrics.size(Qt::TextSingleLine, QString(f[i][j]));
                str = QString::fromWCharArray(&f[i][j], 1);
                if(status == alphaSet && newAlpha == solver::Coords(j, i))
                    painter.setPen(naPen);
                painter.drawText(x + ((s - csize.width())>>1) + 1,
                                 y + ((s - csize.height())>>1) + metrics.ascent() + 1,
                                 str);
                painter.setPen(normPen);
            }
        }
}

void QAlphaField::mousePressEvent(QMouseEvent *event)
{
    int cs = cellSize();
    chosenX = event->x() / cs;
    chosenY = event->y() / cs;

    if((event->buttons() & Qt::LeftButton))
    {
        if(!checkCoords(chosenX, chosenY)) return;
        switch(status)
        {
            case afterReset:
                if (!ch)
                    ch = new QAlphaChooser;
                ch->move(event->globalPos());
                connect(ch, SIGNAL(clicked(QChar)), this, SLOT(updateAlpha(QChar)));
                ch->show();
            break;
            case alphaSet:
            {
                sel.push_back(solver::Coords(chosenX, chosenY));
                updateString();
                update();
            }
            break;
            default:
            break;
        }
    }
    else if((event->buttons() & Qt::RightButton) && status == alphaSet)
    {
        if(solver::Coords(chosenX, chosenY) == newAlpha)
        {
            updateAlpha(L' ');
            sel.clear();
            status = afterReset;
            updateString();
            update();
        }
        else if(sel.size() && *(sel.end() - 1) == solver::Coords(chosenX, chosenY))
        {
            sel.erase(sel.end() - 1);
            updateString();
            update();
        }
    }
}

void QAlphaField::mouseMoveEvent(QMouseEvent *event)
{
    int cs = cellSize();
    chosenX = event->x() / cs;
    chosenY = event->y() / cs;
    repaint();
}

void QAlphaField::updateAlpha(QChar c)
{
    if((c >= (QChar)0x430 && c <= (QChar)0x44f) || c == L' ')
    {
        wchar_t s[2];
        QString(c).toWCharArray(s);
        switch(status)
        {
            case afterReset:
                f[chosenY][chosenX] = *s;
                status = alphaSet;
                sel.clear();
                newAlpha = solver::Coords(chosenX, chosenY);
            break;
            case alphaSet:
                if(c == L' ')
                    f[chosenY][chosenX] = L' ';
            default:
            break;
        }
    }

    if(ch) ch->hide();
    update();
}

const solver::Field &QAlphaField::getField()
{
    return f;
}

void QAlphaField::selReset()
{
    newAlpha.x = newAlpha.y = -1;
    sel.clear();
    status = afterReset;
    emit stringChanged(QString(""));
}

bool QAlphaField::checkCoords(int x, int y)
{
    switch(status)
    {
        case afterReset:
        if(f[y][x] != L' ' || !f.isLinked(x,y))
            return false;
        break;
        case alphaSet:
        {
            int dist_end;
            if(std::find(sel.begin(), sel.end(), solver::Coords(chosenX, chosenY)) != sel.end())
                return false;

            dist_end = (sel.size() ? (sel.end()-1)->distance(x, y) : 1);

            if(f[y][x] == L' ' ||
               dist_end != 1)
            return false;
        }
        break;
        default:
        return false;
    }
    return true;
}

void QAlphaField::updateString()
{
    if(sel.size())
    {
        wchar_t *str = new wchar_t[25];
        if(!str) return;
        deque<solver::Coords>::iterator it;
        wchar_t *s = str;
        for( it = sel.begin(); it != sel.end(); ++it)
            *s++ = f[*it];
        *s = (wchar_t)0;
        emit stringChanged(QString::fromWCharArray(str));
        delete str;
    }
    else
        emit stringChanged(QString(""));
}

void QAlphaField::restartLastConf()
{
    if(status == alphaSet)
        f[newAlpha] = L' ';
    selReset();
}

void QAlphaField::fullReset()
{
    f.Create(5,5);
    status = afterReset;
    sel.clear();
}

bool QAlphaField::isValidWord()
{
    if(status == alphaSet && std::find(sel.begin(), sel.end(), newAlpha) != sel.end())
        return true;

    return false;
}

void QAlphaField::setSel(const deque<solver::Coords> &st)
{
    sel = st;
}
